#include "wxsversionconverter.h"
#include "wxsproject.h"
#include "wxscoder.h"

#include <globals.h>
#include <wx/string.h>
#include <tinywxuni.h>

const wxsVersionConverter& wxsVersionConverter::Get() {
  static wxsVersionConverter Singleton;
  return Singleton;
}

bool wxsVersionConverter::DetectOldConfig( TiXmlElement* Node, M_Code_unused wxsProject* Project ) const {
  if( Node->FirstChildElement( "dialog" ) ) {
    return true;
  }
  if( Node->FirstChildElement( "frame" ) ) {
    return true;
  }
  if( Node->FirstChildElement( "panel" ) ) {
    return true;
  }
  if( Node->FirstChildElement( "configuration" ) ) {
    return true;
  }
  return false;
}

TiXmlElement* wxsVersionConverter::ConvertFromOldConfig( TiXmlElement* ConfigNode, TiXmlDocument* Doc, wxsProject* Project ) const {
  if( msging( _T( "This project uses old wxSmith configuration format\n"
                  "Would you like me to convert to new one?\n" ),
              _T( "wxSmith: Converting from old format" ),
              wxYES_NO ) != wxID_YES ) {
    return 0;
  }
  TiXmlElement* NewConfig = Doc->InsertEndChild( TiXmlElement( "wxSmith" ) )->ToElement();
  TiXmlElement* Resources = NewConfig->InsertEndChild( TiXmlElement( "resources" ) )->ToElement();
  for( TiXmlElement* Node = ConfigNode->FirstChildElement(); Node; Node = Node->NextSiblingElement() ) {
    wxString NodeName = cbC2U( Node->Value() );
    if( NodeName == _T( "configuration" ) ) {
      const char* AppSrc  = Node->Attribute( "app_src_file" );
      const char* Main    = Node->Attribute( "main_resource" );
      const char* InitAll = Node->Attribute( "init_all_handlers" );
      if( AppSrc ) {
        TiXmlElement* GUINode = NewConfig->InsertEndChild( TiXmlElement( "gui" ) )->ToElement();
        GUINode->SetAttribute( "name", "wxWidgets" );
        GUINode->SetAttribute( "src", AppSrc );
        GUINode->SetAttribute( "main", Main ? Main : "" );
        GUINode->SetAttribute( "init_handlers", InitAll ? InitAll : "necessary" );
        GUINode->SetAttribute( "language", "CPP" );
      }
    } else {
      if( NodeName == _T( "dialog" ) ||
          NodeName == _T( "frame" ) ||
          NodeName == _T( "panel" ) ) {
        const char* Wxs   = Node->Attribute( "wxs_file" );
        const char* Class = Node->Attribute( "class" );
        const char* Src   = Node->Attribute( "src_file" );
        const char* Hdr   = Node->Attribute( "header_file" );
        const char* Xrc   = Node->Attribute( "xrc_file" );
        const char* Mode  = Node->Attribute( "edit_mode" );
        if( Wxs && Class && Src && Hdr && Mode ) {
          if( cbC2U( Mode ) == _T( "Source" ) ) {
            Xrc = 0;
          }
          TiXmlElement* Res = Resources->InsertEndChild( TiXmlElement(
                                NodeName == _T( "dialog" ) ? "wxDialog" :
                                NodeName == _T( "frame" )  ? "wxFrame" :
                                "wxPanel" ) )->ToElement();
          Res->SetAttribute( "wxs", cbU2C( _T( "wxsmith/" ) + cbC2U( Wxs ) ) );
          Res->SetAttribute( "src", Src );
          Res->SetAttribute( "hdr", Hdr );
          if( Xrc ) {
            Res->SetAttribute( "xrc", Xrc );
          }
          Res->SetAttribute( "name", Class );
          Res->SetAttribute( "language", "CPP" );
          ConvertOldWxsFile( Project->GetProjectPath() + _T( "wxsmith/" ) + cbC2U( Wxs ), Xrc != 0 );
          AdoptOldSourceFile( Project->GetProjectPath() + cbC2U( Src ), cbC2U( Class ) );
        }
      }
    }
  }
  return NewConfig;
}

void wxsVersionConverter::ConvertOldWxsFile( const wxString& FileName, bool UsingXrc ) const {
  TiXmlDocument Doc;
  if( !TinyXML::LoadDocument( FileName, &Doc ) ) {
    return;
  }
  TiXmlElement* Smith = Doc.FirstChildElement( "resource" );
  if( Smith ) {
    Smith->SetValue( "wxsmith" );
  }
  if( UsingXrc && Smith ) {
    TiXmlElement* Resource = Smith->FirstChildElement( "object" );
    TiXmlElement* Extra = Smith->InsertEndChild( TiXmlElement( "resource_extra" ) )->ToElement();
    GatherExtraFromOldResourceReq( Resource, Extra, true );
  }
  TinyXML::SaveDocument( FileName, &Doc );
}

void wxsVersionConverter::GatherExtraFromOldResourceReq( TiXmlElement* Object, TiXmlElement* Extra, bool Root ) const {
  if( !strcmp( Object->Value(), "object" ) ) {
    if( Object->Attribute( "class" ) && ( Root || Object->Attribute( "name" ) ) ) {
      TiXmlElement* ThisExtra = 0;
      if( Object->Attribute( "variable" ) && Object->Attribute( "member" ) ) {
        ThisExtra = Extra->InsertEndChild( TiXmlElement( "object" ) )->ToElement();
        ThisExtra->SetAttribute( "variable", Object->Attribute( "variable" ) );
        ThisExtra->SetAttribute( "member", Object->Attribute( "member" ) );
      }
      for( TiXmlElement* Handler = Object->FirstChildElement( "handler" ); Handler; Handler = Handler->NextSiblingElement( "handler" ) ) {
        if( !ThisExtra ) {
          ThisExtra = Extra->InsertEndChild( TiXmlElement( "object" ) )->ToElement();
        }
        ThisExtra->InsertEndChild( *Handler );
      }
      if( ThisExtra ) {
        if( Root ) {
          ThisExtra->SetAttribute( "root", "1" );
        } else {
          ThisExtra->SetAttribute( "name", Object->Attribute( "name" ) );
          ThisExtra->SetAttribute( "class", Object->Attribute( "class" ) );
        }
      }
    }
  }
  for( TiXmlElement* Child = Object->FirstChildElement(); Child; Child = Child->NextSiblingElement() ) {
    GatherExtraFromOldResourceReq( Child, Extra, false );
  }
}

void wxsVersionConverter::AdoptOldSourceFile( const wxString& FileName, const wxString& Class ) const {
  bool IsInternalHeaders = wxsCoder::Get()->GetCode(
                             FileName,
                             _T( "//(*InternalHeaders(" ) + Class + _T( ")\n" ),
                             _T( "//*)" ),
                             true, true ).Length() != 0;
  bool IsIdInit = wxsCoder::Get()->GetCode(
                    FileName,
                    _T( "//(*IdInit(" ) + Class + _T( ")\n" ),
                    _T( "//*)" ),
                    true, true ).Length() != 0;
  if( !IsInternalHeaders || !IsIdInit ) {
    wxFontEncoding Encoding;
    bool UseBOM;
    wxString Content = wxsCoder::Get()->GetFullCode( FileName, Encoding, UseBOM );
    int Pos = 0;
    if( !IsInternalHeaders ) {
      while( Pos < ( int )Content.Len() && LineContainDirectivesOnly( Content, Pos ) );
      wxString AddInternalHeaders =
        _T( "//(*InternalHeaders(" ) + Class + _T( ")\n" )
        _T( "//*)\n" )
        _T( "\n" );
      Content = Content.Mid( 0, Pos ) + AddInternalHeaders + Content.Mid( Pos );
    } else {
      Pos = Content.Find( _T( "//(*InternalHeaders(" ) + Class + _T( ")\n" ) );
      int Shift = Content.Mid( Pos ).Find( _T( "//*)" ) );
      if( Shift != wxNOT_FOUND ) {
        Pos += Shift;
      }
    }
    if( !IsIdInit ) {
      int NewPos = Content.Find( _T( "BEGIN_EVENT_TABLE(" ) + Class );
      if( NewPos != wxNOT_FOUND ) {
        Pos = NewPos;
      }
      wxString Indent;
      while( Pos > 0 &&
             Content.GetChar( Pos - 1 ) != _T( '\n' ) &&
             Content.GetChar( Pos - 1 ) != _T( '\r' ) ) {
        wxChar Ch = Content.GetChar( Pos-- );
        Indent.Append( ( Ch == _T( '\t' ) ) ? _T( '\t' ) : _T( ' ' ) );
      }
      wxString AddIdInit =
        Indent + _T( "//(*IdInit(" ) + Class + _T( ")\n" ) +
        Indent + _T( "//*)\n" ) +
        Indent + _T( "\n" );
      Content = Content.Mid( 0, Pos ) + AddIdInit + Content.Mid( Pos );
    }
    wxsCoder::Get()->PutFullCode( FileName, Content, Encoding, UseBOM );
  }
}

bool wxsVersionConverter::LineContainDirectivesOnly( const wxString& Code, int& BeginPos ) const {
  int Pos = BeginPos;
  wxChar PreviousChar = _T( '\0' );
  while( Pos < ( int )Code.Len() ) {
    wxChar Ch = Code.GetChar( Pos );
    if( Ch == _T( '/' ) && PreviousChar == _T( '/' ) ) {
      while( ++Pos < ( int )Code.Len() ) {
        PreviousChar = Ch;
        wxChar _Ch = Code.GetChar( Pos );
        if( _Ch == _T( '\n' ) || _Ch == _T( '\r' ) ) {
          if( PreviousChar == _T( '\\' ) ) {
            if( ++Pos < ( int )Code.Len() ) {
              PreviousChar = _Ch;
              _Ch = Code.GetChar( Pos );
              if( ( _Ch != _T( '\n' ) && _Ch == _T( '\r' ) ) || ( _Ch == PreviousChar ) )
              { --Pos; }
            }
          } else {
            while( Pos < ( int )Code.Len() ) {
              wxChar __Ch = Code.GetChar( Pos );
              if( __Ch != _T( '\n' ) && __Ch != _T( '\r' ) )
              { break; }
              Pos++;
            }
            BeginPos = Pos;
            return true;
          }
        }
      }
      BeginPos = Pos;
      return false;
    } else if( Ch == _T( '*' ) && PreviousChar == _T( '/' ) ) {
      while( ++Pos < ( int )Code.Len() ) {
        PreviousChar = Ch;
        wxChar _Ch = Code.GetChar( Pos );
        if( _Ch == _T( '/' ) && PreviousChar == _T( '*' ) ) {
          break;
        }
      }
      if( Pos >= ( int )Code.Len() ) {
        break;
      }
    } else if( PreviousChar == _T( '/' ) ) {
      Pos--;
      break;
    } else if( Ch != _T( ' ' ) && Ch != _T( '\t' ) ) {
      if( Ch != _T( '/' ) ) {
        break;
      }
    }
    PreviousChar = Ch;
    Pos++;
  }
  if( Pos < ( int )Code.Len() ) {
    wxChar Ch = Code.GetChar( Pos );
    if( ( Ch != _T( '\n' ) ) && ( Ch != _T( '\r' ) ) ) {
      if( Ch != _T( '#' ) ) {
        return false;
      }
    }
  }
  bool BlockMultilineComment = false;
  while( Pos < ( int )Code.Len() ) {
    wxChar Ch = Code.GetChar( Pos );
    if( Ch == _T( '\n' ) || Ch == _T( '\r' ) ) {
      if( PreviousChar == _T( '\\' ) ) {
        if( ++Pos >= ( int )Code.Len() ) {
          BeginPos = Pos;
          return false;
        }
        PreviousChar = Ch;
        Ch = Code.GetChar( Pos );
        if( ( Ch != _T( '\n' ) && Ch != _T( '\r' ) ) || ( Ch == PreviousChar ) ) {
          Pos--;
        }
      } else
      { break; }
    } else if( Ch == _T( '/' ) && PreviousChar == _T( '/' ) ) {
      BlockMultilineComment = true;
    } else if( Ch == _T( '*' ) && PreviousChar == _T( '/' ) && !BlockMultilineComment ) {
      bool WasAnyNL = false;
      while( ++Pos < ( int )Code.Len() ) {
        PreviousChar = Ch;
        Ch = Code.GetChar( Pos );
        if( Ch == _T( '/' ) && PreviousChar == _T( '*' ) ) {
          if( WasAnyNL ) {
            BeginPos = ++Pos;
            return true;
          } else
          { break; }
        } else if( Ch == _T( '\n' ) || Ch == _T( '\r' ) ) {
          WasAnyNL = true;
        }
      }
    }
    PreviousChar = Ch;
    Pos++;
  }
  while( Pos < ( int )Code.Len() ) {
    wxChar Ch = Code.GetChar( Pos );
    if( Ch != _T( '\n' ) && Ch != _T( '\r' ) ) {
      break;
    }
    Pos++;
  }
  BeginPos = Pos;
  return true;
}


TiXmlElement* wxsVersionConverter::Convert( M_Code_unused TiXmlElement* ConfigNode, M_Code_unused TiXmlDocument* Doc, M_Code_unused wxsProject* Project ) const {
  return 0;
}
